#include "constants.h"

private ["_debug","_can_run","_player_number","_base_wait_time","_wait_time","_enycamps","_enycamp","_units","_alive_units","_vec_types","_alive_vehicles"];
_debug = false;
_can_run = true;
_player_number = 0;
_base_wait_time = 130; // Default is 150
_enycamp_lifetime = 1200; // Default is 1200, respawning every 20 minutes if not attacked.
// Contains a list of all enycamps which have been spawn.
// One enycamp is described with: [Zone,SpawnTime,StartPos,ObjectName,[unit1,unit2,unit3,..]]
_enycamps = [];
_sector_spawn_times = [];
_own_side = switch (d_own_side) do {case "EAST": {east};case "WEST": {west};case "GUER": {resistance};case "CIV": {civilian}; default{west};};

if (typeName _this == "ARRAY") then {
	if (count _this > 0) then {	_debug = _this select 0; };
};
If (isNil "D_LAST_RANDOM_SPAWN_ID") then {D_LAST_RANDOM_SPAWN_ID = 1;};

// Checks that no player is around, then destroy all the given units and their crew
// Everything will be cleaned up automatically through the clean up system.
_fnc_delayed_destroy = {
	private ["_wait","_units","_objs","_unit","_found","_alive","_position"];
	_wait = true;
	_units = _this select 0;
	_objs = _this select 1;
	while {_wait} do {
		_alive = false;
		{
			_unit = _x;
			if (!isNull _unit) then {
				_position = getPos _unit;
				if (_position distance [0,0,0] > 1) then {
					_found = false;
					{
						if (_x distance _position < 600) exitWith {_found = true; };
					} foreach playableUnits;
					if (_found) then {
						_alive = true;
					} else {
						deleteVehicle _unit;
					};
				} else {
					deleteVehicle _unit;
				};
			};
		} foreach _units;
		if (_alive) then {
			sleep (150 + random(100));
		} else {
			{ deleteVehicle _x;
			} foreach _objs;
			diag_log Format["(%1)[%2]: %3",diag_tickTime,"randomcamps.sqf","Delayed Destroy Done"];
			_wait = false;
		};
	};
};

_fnc_generate_object_pos = {
	private ["_center","_start_angle","_end_angle","_start_radius","_end_radius","_angle","_radius","_nx","_ny"];
	_center = _this select 0;
	_start_angle = _this select 1;
	_end_angle = _this select 2;
	_start_radius = _this select 3;
	_end_radius = _this select 4;

	_angle = _start_angle + random (_end_angle - _start_angle);
	_radius = _start_radius + random (_end_radius - _start_radius);
	_nx = (_center select 0) + _radius * sin _angle;
	_ny = (_center select 1) + _radius * cos _angle;
	[_nx, _ny, 0];
};


while {_can_run} do {
	if (_debug) then { diag_log Format["(%1)[%2]: %3 (%4)",diag_tickTime,"randomcamps.sqf","Starting",Time];};
	if (WithRandomCamps)then {
		_max_enycamps = RandomCampsCount;

		// We will spawn two enycamps each time. One in the north of the island, the other in the south part
		// The code will check every now and then if a enycamp needs to be spawned or not.
		_player_number = call XPlayersNumber;
		if (_player_number > 0) then {
			if (_debug) then { diag_log ""; diag_log ""; diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"randomcamps.sqf","Players online",_player_number,(count _enycamps)];};
			
			// ----------------------------------------------------------------------
			//   Check current enycamps, and queue for delete those which are down
			// ----------------------------------------------------------------------
			If ((count _enycamps) > 0) then {
				if (_debug) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"randomcamps.sqf","Checking active enycamps",_enycamps];};
				for [{_i = 0},{_i < count _enycamps},{_i = _i + 1}] do {
					_enycamp = _enycamps select _i;
					_units = _enycamp select XF_SPAWN_ARRAY_UNITSARRAY;
					_objs = _enycamp select XF_SPAWN_ARRAY_OBJECTSARRAY;
					_alive_vehicles = [];
					_alive_units = [];
					{
						if (!IsNull _x) then {
							// An alive unit which cannot move or which is too wounded... doesn't interest us.
							if ((alive _x) && (canMove _x) && (damage _x < 0.5)) then {
								if (_x isKindOf "LandVehicle") then {
									_alive_vehicles set [count _alive_vehicles, typeOf(_x)];
								} else {
									if (_x isKindOf "Man") then {
										_alive_units set [count _alive_units, typeOf(_x)];
									};
								};
							};
						};
					}foreach _units;
					// We check both vehicles and units in this case.
					_queue_for_deletion = false;
					if (count _alive_units > 0) then {
						if (_debug) then { diag_log Format["(%1)[%2]: %3: %4 - %5",diag_tickTime,"randomcamps.sqf","Alive",_alive_vehicles,_alive_units];};
					} else {
						_queue_for_deletion = true;
					};
					// We check if the enycamp is active for more than 30 minutes. If it is the case... we'll delete it and spawn another one.
					If (!_queue_for_deletion && (_enycamp select XF_SPAWN_ARRAY_SPAWNTIME < (Time - _enycamp_lifetime))) then {
						if (_debug) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"randomcamps.sqf","enycamp exceeded life time",_enycamp];};
						_queue_for_deletion = true;
					};
					if (_queue_for_deletion) then {
						if (_debug) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"randomcamps.sqf","Queued for deletion",_enycamp];};
						_sector = _enycamp select XF_SPAWN_ARRAY_SECTOR;
						_wait_time = switch (true) do {
							case (_player_number <= 5):  {_base_wait_time * 12};
							case (_player_number <= 10): {_base_wait_time * 8};
							case (_player_number <= 15): {_base_wait_time * 4};
							case (_player_number <= 20): {_base_wait_time * 2};
							default {_base_wait_time};
						};
						_sector_spawn_times set [_sector, (time + _wait_time)];
						_markers = _enycamp select XF_SPAWN_ARRAY_MARKERSARRAY;
						{ deleteMarkerLocal _x;						
						} foreach _markers;
						[_units, _objs] call _fnc_delayed_destroy;
						_enycamps set [_i,"XxDelxX"];
					};
				};
				_enycamps = _enycamps - ["XxDelxX"];
			};
			// ----------------------------------------------------------------------
			//   Spawn more enycamps if needed.
			// ----------------------------------------------------------------------
			If ((count _enycamps) < _max_enycamps) then {
				// We filter out only the sectors which are designed for random spawn
				_spawn_sectors = [d_map_sectors,[XF_SECTOR_ARRAY_TYPE,"==",XF_SECTOR_TYPE_SPAWN]] call XfTableFilter;
				if (_debug) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"randomcamps.sqf","Spawn Sectors",_spawn_sectors];};
				// Next we check which sectors are already active.
				_current_sectors = [];
				{	_sector = _x select XF_SPAWN_ARRAY_SECTOR;
					if !(_sector in _current_sectors) then {_current_sectors set [count _current_sectors, _sector];};
				} foreach _enycamps;
				// If there are as many active sectors as possible sectors, we just pick one randomly.
				_sector = -1;
				if ((count _current_sectors) >= (count _spawn_sectors)) then {
					_sector = floor(random(count _spawn_sectors));
				} else {
					_select_sectors = [];
					for [{_i = 0},{_i < count _spawn_sectors},{_i = _i + 1}] do {
						if !(_i in _current_sectors) then {_select_sectors set [count _select_sectors, _i];};
					};
					_sector = _select_sectors call XfRandomArrayVal;
				};
				// Now we should have a valid sector
				if (_sector >= 0 && _sector < count _spawn_sectors) then {
					// Next thing, we check when a enycamp was last spawned in this sector.
					_next_spawn = 0;
					if (_sector < count _sector_spawn_times) then { _next_spawn = _sector_spawn_times select _sector};
					if (isNil "_next_spawn") then {_next_spawn = 0};
					// If the spawn interval has passed, we can spawn a new one.
					if (_debug) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"randomcamps.sqf","Selected sector",_sector,_next_spawn];};
					if (time > _next_spawn) then {
						_markers = [];
						_marker_id = D_LAST_RANDOM_SPAWN_ID;
						D_LAST_RANDOM_SPAWN_ID = D_LAST_RANDOM_SPAWN_ID + 1;
						
						// Ok... now we can try to spawn a enycamp :-)
						_spawn_sector = _spawn_sectors select _sector;
						_shape_arrays = _spawn_sector select XF_SECTOR_ARRAY_SHAPE_ARRAY;
						if (_debug) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"randomcamps.sqf","Sector Shapes",_shape_arrays];};
						_grp = objNull;
						_enycamp = [];
						_avoid_spot = [d_map_locations, [XF_MAP_LOCATION_TYPE,"IN",[XF_MAP_LOCATION_TYPE_TOWN,XF_MAP_LOCATION_TYPE_VILLAGE,XF_MAP_LOCATION_TYPE_INDUSTRIAL,XF_MAP_LOCATION_TYPE_SPECIAL]]] call XfTableFilter;
						if (_debug) then { diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"randomcamps.sqf","Avoid Spots",count _avoid_spot,_avoid_spot];};
						// We filter out all those not in the right sector.
						for [{_i = 0},{_i < count _avoid_spot},{_i = _i + 1}] do {
							_location_center = (_avoid_spot select _i) select XF_MAP_LOCATION_CENTER;
							_not_found = true;
							{ if ([_location_center, _x] call XfPointInShape) exitWith {_not_found = false;};
							} foreach _shape_arrays;
							if (_not_found) then { _avoid_spot set [_i, "XxDelxX"]; };
						};
						_avoid_spot = _avoid_spot - ["XxDelxX"];
						if (_debug) then { diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"randomcamps.sqf","Avoid Spots",count _avoid_spot,_avoid_spot];};

						_tries = 40;
						// Position, Slope, Units Density
						// A density of 400 means that we have a player less than 75m from the spot.
						_best_spot = [[],2000,400,[]];
						_spot = [];
						while {_tries > 0} do {
							// We select a random shape among those of the in the shapes
							_shape_array = _shape_arrays call XfRandomArrayVal;
							_spot = [];
							_roads = [];
							_tries2 = 50;
							While {(count _spot < 3) && (count _roads <= 0) && (_tries2 > 0)} do {
								_spot = [_shape_array,30] call XfGetRanPointShapeBig;
								if (count _spot > 0) then {
									if ((_spot distance FLAG_BASE) < 600) then { _spot = []; };
								};
								if (count _spot > 0) then {
									// 1) We test if it is far away from all mission spots
									_test_spot = _spot;
									_reason = "";
									_ok = true;
									{ 
										_location_center = _x select XF_MAP_LOCATION_CENTER;
										_location_size = _x select XF_MAP_LOCATION_SIZE;
										_minimum_dist = (_location_size * 2);
										if (_minimum_dist < 400) then { _minimum_dist = 400 };
										if ((_location_center distance _spot) < _minimum_dist) exitWith { _ok = false; _reason = _x select XF_MAP_LOCATION_UNIQUENAME;};
									} foreach _avoid_spot;
									sleep 0.2;
									if (_ok) then {
										if (_debug) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"randomcamps.sqf","Far Spot", _spot]; };
										_roads = (_spot nearRoads 100);
										if (count _roads == 0) then {
											// If no roads found, we extend the search, then we need to check again if it's not near an avoid spot.
											_roads = (_spot nearRoads 200);
											if (count _roads == 0) then { _roads = (_spot nearRoads 300); };
											if (count _roads > 0) then {
												if (_debug) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"randomcamps.sqf","Extended Road Check", count _roads]; };
												for [{_i = 0},{_i < count _roads},{_i = _i + 1}] do {
													_road = _roads select _i;
													{ 
														_location_center = _x select XF_MAP_LOCATION_CENTER;
														_location_size = _x select XF_MAP_LOCATION_SIZE;
														_minimum_dist = (_location_size * 2);
														if (_minimum_dist < 400) then { _minimum_dist = 400 };
														if ((_location_center distance getPos(_road)) < _minimum_dist) exitWith { _roads set [_i,"XxDelxX"];};
													} foreach _avoid_spot;
													sleep 0.2;
												};
												_roads = _roads - ["XxDelxX"];
												if (_debug) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"randomcamps.sqf","Extended Road Check Done", count _roads]; };
											};
										};
										if (count _roads > 0) then {
											{
												// We check each roads to see it they fit the enycamp size (no obstacles).
												_road = _x;
												_spot = [(getPos _road),50,30] call XfGetRanPointCircleBig;
												if (count _spot == 3) exitWith{};
											} foreach _roads;
											sleep 0.2;
											if (count _spot < 3) then { _reason = "NoRoom";};
										} else {
											_reason = "NoRoads";
											_spot = [];
										};
										_tries2 = _tries2 - 1;
									} else {
										_spot = [];
									};
									if (count _spot == 3) then { _reason = "OK!" };
									if (_debug) then { _markers set [count _markers, ([Format["mkr_tested_spot_%1_%2",_marker_id,(_test_spot select 0)],_test_spot, "ICON", "ColorBlue", [0.2,0.2],_reason,0,"hd_unknown","",0.5] call XfCreateMarkerLocal)]; };
									sleep 0.2;
								} else {
									_tries2 = _tries2 - 1;
								};
							};
							if (count _spot > 0) then {
								if ((_spot distance FLAG_BASE) < 600) then { _spot = []; };
							};
							// We search a spot which has roads nearby
							if (count _spot == 3) then {
								if (_debug) then { diag_log Format["(%1)[%2]: %3: %4 %5",diag_tickTime,"randomcamps.sqf","Testing spot", _spot, _shape_array]; };
								_lst = nearestObjects [_spot, ["Man","Car","Tank","Ship"], 500];
								_density = 0;
								{
									if ((side _x) == _own_side) then {
										if (_debug) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"randomcamps.sqf","Own Side", _x,(isplayer _x)]; };
										if (isplayer _x) then {
											// If a humen player is present, then we highly increase the density to reject as much as possible those spots
											_density = _density + (30000 / (_x distance _spot));
										} else {
											_density = _density + (6000 / (_x distance _spot));
										};
									};
									sleep 0.01;
								} foreach _lst;
								// We factor in the fact many units are present, to avoid spawning in an already crowded place
								_density = _density + (count _lst) * 10;
								_slope = [_spot, 20] call XfGetSlope;
								// 1) We test if the density is better or within range
								// 2) We test if the slope is either better... or within range of a good value
								if (((_density < (_best_spot select 2)) || (abs(_density - (_best_spot select 2)) < 100)) && ((_slope < (_best_spot select 1)) ||  ((abs(_slope - (_best_spot select 1))) < 5))) then {
									// We update the best spot
									_best_spot = [_spot, _slope, _density,_town_pos];
									// We lower the tries count to narrow a good spot more quickly
									_tries = _tries - 3;
									if (_debug) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"randomcamps.sqf","New best spot", _best_spot,_tries]; };
								};
							};
							_tries = _tries - 1;
							sleep 0.265;
						};
						if (_debug) then {diag_log ""; diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"randomcamps.sqf","Selected spot",_best_spot];};
						_spot = _best_spot select 0;
						If (count _spot == 3) then {
							if (_debug) then { _markers set [count _markers, ([Format["mkr_tested_spot_%1",_marker_id],_spot, "ICON", "ColorRed", [1.1,1.1],"",0,"mil_dot","",1] call XfCreateMarkerLocal)]; };
							// From here, we know approximatively where to start.
							// We have a road object with normally enough room around it for the camp.
							// Next step, finding the road items which are the most far from the original item.
							_roads = (_spot nearRoads 70);
							if (count _roads > 0) then {
								// The following calculation tries to find an empty zone outside of the roads but still next to them.
								_test_spot = [];
								_tries2 = 30;
								while {(count _test_spot < 3) && (_tries2 > 0) } do {
									_test_spot = [_spot,70,20] call XfGetRanPointCircleBig;
									if (count _test_spot > 0) then {
										{ if ((_test_spot distance (getPos _x)) < 15) exitWith {_test_spot = [];};
										} foreach _roads;
									};
									_tries2 = _tries2 - 1;
								};
								if (count _test_spot == 3) then {
									if (_debug) then { _markers set [count _markers, ([Format["mkr_found_spot_%1",_marker_id],_spot, "ICON", "ColorBlack", [0.7,0.7],"",0,"mil_triangle","",1] call XfCreateMarkerLocal)]; };
									_spot = _test_spot;
								};
							} else {
								// No other roads... sounds impossible but whatever... let's spawn in place ;-)
							};
						};
						If (count _spot == 3) then {
							_units = [d_enemy_side_char,1,floor(random(5))] call XfMissionGetRandomFootmenUnits;
							_unit = [d_enemy_side_char,1,XF_VEHICLE_TYPES_LIGHTWHEEL] call XfMissionGetRandomVehicleUnits;
							_units = [_unit, _units] call XfArrayPushStack;
							if (count _units > 0) then {
								if (_debug) then { diag_log Format["(%1)[%2]: %3: %4 %5 %6",diag_tickTime,"randomcamps.sqf","Spawning camp",_spot,_units,isnil "_fnc_generate_object_pos"]; };
								
								_enycamp_units = [];
								_grp = [d_enemy_side] call x_creategroup;
								_grp setBehaviour "AWARE";
								_grp setFormation "LINE";
								_grp allowFleeing 0;
								_leader = objNull;
								_veh = objNull;
								_objs = [];

								// 1) spawn the camp itself
								// _objs = [_spot, random 360, "camp_us1"] call BI_fnc_objectMapper;
								_obj = createVehicle ["Land_Campfire_burning", _spot, [], 3, "NONE"];
								_objs set [count _objs, _obj];
								_ammo_type = switch (d_enemy_side) do {
									case "WEST": {"USBasicWeaponsBox"};
									case "EAST": {"RUBasicWeaponsBox"};
									default {"LocalBasicWeaponsBox"};
								};
								
								_pos = [_spot, 1, 35, 5, 10] call _fnc_generate_object_pos;
								_obj = createVehicle [_ammo_type, _pos, [], 3, "NONE"];
								_obj setDir random(360);
								_objs set [count _objs, _obj];
								_ammo_type = switch (d_enemy_side) do {
									case "WEST": {"USBasicAmmunitionBox"};
									case "EAST": {"RUBasicAmmunitionBox"};
									default {"LocalBasicAmmunitionBox"};
								};
								_pos = [_spot, 45, 80, 5, 10] call _fnc_generate_object_pos;
								_obj = createVehicle [_ammo_type, _pos, [], 3, "NONE"];
								_obj setDir random(360);
								_objs set [count _objs, _obj];
								_pos = [_spot, 90, 135, 5, 10] call _fnc_generate_object_pos;
								_obj = createVehicle ["Land_A_tent", _pos, [], 3, "NONE"];
								_obj setDir random(360);
								_objs set [count _objs, _obj];
								_pos = [_spot, 180, 225, 5, 10] call _fnc_generate_object_pos;
								_obj = createVehicle ["Land_A_tent", _pos, [], 3, "NONE"];
								_obj setDir random(360);
								_objs set [count _objs, _obj];
								_pos = [_spot, 270, 315, 5, 10] call _fnc_generate_object_pos;
								_obj = createVehicle ["Land_A_tent", _pos, [], 3, "NONE"];
								_obj setDir random(360);
								_objs set [count _objs, _obj];
								
								// Correcting the angle if it is too awkward
								{ if (((vectorUp _x) distance [0,0,1]) > 2) then { _x setVectorUp [0,0,1];};
								} foreach _objs;
								
								// 2) Spawn the units
								{
									if (_x isKindOf "Man") then {
										_veh = [_x, _grp, _spot, 10] call XfCreateUnit;
										// _veh = _grp createUnit [_x, _spot, [], 10, "NONE"];
									} else {
										_veh = createVehicle [_x, _spot, [], 10, "NONE"];
										if (((_veh isKindOf "Car") && (d_LockCars>0)) || ((_veh isKindOf "Tank") && (d_LockArmored>0)) || ((_veh isKindOf "Air") && (d_LockAir>0))) then {
											_veh lock true;
										};
									};
									_enycamp_units set [count _enycamp_units, _veh];
									if !(_veh isKindOf "Man") then {
										_crew = [_veh, _grp] call XfSpawnCrew;
										{ _enycamp_units set [count _enycamp_units, _x];
										} foreach crew _veh;
									};
									if (isNull _leader) then {
										_leader = leader _grp;
										_leader setRank "COLONEL";
									};
									sleep 1;
								} foreach _units;
								{ deleteMarkerLocal _x;
								}foreach _markers;
								if (count _enycamp_units > 0) then {
									[_grp, _spot,false] spawn XfTaskDefend;
									_enycamp set [XF_SPAWN_ARRAY_SECTOR,_sector];
									_enycamp set [XF_SPAWN_ARRAY_SPAWNTIME,Time];
									_enycamp set [XF_SPAWN_ARRAY_STARTPOS,_spot];
									_enycamp set [XF_SPAWN_ARRAY_OBJECTNAME,Format["enycamp%1",_marker_id]];
									_enycamp set [XF_SPAWN_ARRAY_RADIUS,0];
									_enycamp set [XF_SPAWN_ARRAY_GROUP,_grp];
									_enycamp set [XF_SPAWN_ARRAY_UNITSARRAY,_enycamp_units];
									_enycamp set [XF_SPAWN_ARRAY_OBJECTSARRAY,_objs];
									_markers set [count _markers, ([(_enycamp select XF_SPAWN_ARRAY_OBJECTNAME),_spot, "ICON", "ColorRed", [0.9,0.9],(_enycamp select XF_SPAWN_ARRAY_OBJECTNAME),0,"hd_flag","",0.2] call XfCreateMarkerLocal)];
									_enycamp set [XF_SPAWN_ARRAY_MARKERSARRAY,_markers];
								};
							};
						} else {
							if (_debug) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"randomcamps.sqf","No spawn spot!",_spot]; };
						};
						if (count _enycamp > 0) then {
							if (_debug) then { diag_log Format["(%1)[%2]: %3: %4",diag_tickTime,"randomcamps.sqf","Spawned enycamp",_enycamp]; };
							_enycamps set [count _enycamps, _enycamp];
						} else {
							{ deleteMarkerLocal _x;
							}foreach _markers;
						};
					} else {
						if (_debug) then { diag_log Format["(%1)[%2]: %3 %4",diag_tickTime,"randomcamps.sqf","Spawn interval not done yet",[time,_next_spawn]];};
						sleep ((_next_spawn - time) / 2);
					};
				} else {
					if (_debug) then { diag_log Format["(%1)[%2]: %3 %4",diag_tickTime,"randomcamps.sqf","No valid sector",[_sector,count _spawn_sectors]];};
					sleep (_base_wait_time * 3);
				};
				// We just tried to spawn a enycamp, we'll check "quickly" if there is another one to spawn.
				sleep (30 + random(30));
			} else {
				if (_debug) then { diag_log Format["(%1)[%2]: %3: %4 (%5)",diag_tickTime,"randomcamps.sqf","Spawn Times",_sector_spawn_times, count _enycamps];};
				sleep (_base_wait_time + random(_base_wait_time / 2));
			};
		} else {
			// Wait 5 mins before checking again if a player is there or not.
			if (_debug) then { diag_log Format["(%1)[%2]: %3",diag_tickTime,"randomcamps.sqf","No players online"];};
			sleep (_base_wait_time * 3);
		};
	} else {
		// Wait 5 minutes before checking again enycamps are activated
		if (_debug) then { diag_log Format["(%1)[%2]: %3",diag_tickTime,"randomcamps.sqf","Random enycamp Disabled"];};
		sleep (_base_wait_time * 3);
	};
};